/** ***************************************************************************
 * CoffeeTimer.hpp
 * 2012 CoffeeParty Ltd.
 * Date Created: 2012-07-21                                  format YYYY-MM-DD
 * Revision Id: $Id$
 ******************************************************************************
 */

#ifndef _COFFEE_TIMER_HPP_
#define _COFFEE_TIMER_HPP_

#include "CoffeeCommons.hpp"

#include <time.h>
#include <sys/time.h>

#include <omp.h>

class CoffeeTimer
{
public:
    CoffeeTimer()
    : virt_timer_speed(1.0f), virt_timer_stop_counter(0),
      start_real_time(0), last_virt_time(0), static_time(0) { }

    ~CoffeeTimer() { }

    unsigned int getTime()
    {
        if (isStopped())
            return last_virt_time;
        return last_virt_time + (unsigned int)((static_time - start_real_time) * virt_timer_speed);
    }

    void initTimer()
    {
        initVirtualTimer();
    }

    void setTime(unsigned int time)
    {
        static_time = getRealTime();
        last_virt_time = time;
        start_real_time = static_time;
    }

    void stopTimer()
    {
        if (!isStopped())
            last_virt_time = getTime();
        --virt_timer_stop_counter;
    }

    void startTimer()
    {
        ++virt_timer_stop_counter;
        if (!isStopped())
            setTime(last_virt_time);
    }

    void setSpeed(float speed)
    {
        setTime(getTime());
        virt_timer_speed = speed;
        if (virt_timer_speed < 0.0f)
            virt_timer_speed = 0.0f;
    }

    float getSpeed()
    {
        return virt_timer_speed;
    }

    bool isStopped()
    {
        return virt_timer_stop_counter < 0;
    }

    void tick()
    {
        static_time = getRealTime();
    }

    unsigned int getRealTime()
    {
        timeval tv;
        gettimeofday(&tv, 0);
        return (unsigned int)(tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    }

    unsigned int getRealTimeInMicroSec()
    {
        timeval tv;
        gettimeofday(&tv, 0);
        return (unsigned int)(tv.tv_sec * 1000000.0f) + (tv.tv_usec);
    }

    unsigned int getRealTimeInMilliSec()
    {
        return getRealTimeInMicroSec() * 0.001; ///< Divide timeMicroSec() by 1000
    }

    unsigned int getRealTimeInSec()
    {
        return getRealTimeInMicroSec() * 0.000001; ///< Divide timeMicroSec() by 1000000
    }

private:
    void initVirtualTimer()
    {
        static_time = getRealTime();
        start_real_time = static_time;
    }

    float virt_timer_speed;
    int virt_timer_stop_counter;
    unsigned int start_real_time;
    unsigned int last_virt_time;
    unsigned int static_time;

};

class CoffeeProfileTimer
{
public:
    CoffeeProfileTimer()
    : start_time(0), stop_time(0), stop_status(false) { }

    ~CoffeeProfileTimer() { }

    void startTimer()
    {
        start_time = (double)omp_get_wtime();
    }

    double getElapsedTime()
    {
        return ((double)omp_get_wtime() - start_time);
    }

    ///< Divide timeMicroSec() by 1000000
    double getTimeInMicroSec()
    {
        return getElapsedTimeInSec() / 1000000.0f;
    }

    ///< Divide timeMicroSec() by 1000
    double getElapsedTimeInMilliSec()
    {
        return getElapsedTimeInSec() / 1000.0f;
    }

    double getElapsedTimeInSec()
    {
        return getElapsedTime();
    }

private:
    double start_time;
    double stop_time;
    bool stop_status;
};

#endif /** _COFFEE_CONSTANTS_HPP_ */
